package com.zys.http.processor;

import cn.hutool.core.text.CharSequenceUtil;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.java.stubs.index.JavaShortClassNameIndex;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.search.GlobalSearchScope;
import com.zys.http.constant.HttpEnum;
import com.zys.http.entity.param.ParamProperty;
import com.zys.http.processor.basic.TypeValueProcessor;
import jdk.jfr.Description;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.stubindex.KotlinClassShortNameIndex;
import org.jetbrains.kotlin.psi.*;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author zys
 * @since 2025-01-22
 */
public abstract class AbstractProcessor implements CoreProcessor {
    @Description("包装类")
    protected static final Map<String, Object> BASIC_DATA_TYPE_OBJECT_MAP = new ConcurrentHashMap<>();
    private static final Pattern GENERICS_PATTERN = Pattern.compile("<(.+?)>");

    static {
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.lang.Boolean", false);
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.lang.String", "");
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.lang.Byte", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.lang.Short", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.lang.Integer", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.lang.Long", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.lang.Float", 0.0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.lang.Double", 0.0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.math.BigInteger", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.math.BigDecimal", 0.0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("boolean", false);
        BASIC_DATA_TYPE_OBJECT_MAP.put("byte", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("short", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("int", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("long", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("float", 0.0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("double", 0.0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("org.springframework.web.servlet.ModelAndView", Collections.emptyMap());
        BASIC_DATA_TYPE_OBJECT_MAP.put("org.springframework.ui.ModelMap", Collections.emptyMap());
        BASIC_DATA_TYPE_OBJECT_MAP.put("java.lang.Object", Collections.emptyMap());

        BASIC_DATA_TYPE_OBJECT_MAP.put("ModelAndView", Collections.emptyMap());
        BASIC_DATA_TYPE_OBJECT_MAP.put("ModelMap", Collections.emptyMap());
        BASIC_DATA_TYPE_OBJECT_MAP.put("Boolean", false);
        BASIC_DATA_TYPE_OBJECT_MAP.put("Byte", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("Int", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("Short", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("Long", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("Float", 0.0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("Double", 0.0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("Char", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("Number", 0);
        BASIC_DATA_TYPE_OBJECT_MAP.put("String", "");
        BASIC_DATA_TYPE_OBJECT_MAP.put("Any", Collections.emptyMap());
        BASIC_DATA_TYPE_OBJECT_MAP.put("JvmType.Object", Collections.emptyMap());
    }

    @Override
    public final Object typeDefaultValue(Cloneable type, Project project) {
        // 实现 java 与 kotlin 类互相引用解析
        return typeDefaultValue(type, project, new HashMap<>());
    }

    protected Object typeDefaultValue(Cloneable type, Project project, Map<String, Integer> recursionMap) {
        if (type instanceof PsiPrimitiveType primitiveType) {
            return psiPrimitiveTypeProcess(primitiveType);
        } else if (type instanceof PsiArrayType arrayType) {
            return psiArrayTypeProcess(arrayType, project, recursionMap);
        } else if (type instanceof PsiClassReferenceType referenceType) {
            return psiClassReferenceTypeProcess(referenceType, project, recursionMap);
        } else if (type instanceof KtTypeReference typeReference) {
            return ktTypeReferenceProcess(typeReference, project, recursionMap);
        }

        return null;
    }

    // region java
    private Object psiPrimitiveTypeProcess(PsiPrimitiveType type) {
        return BASIC_DATA_TYPE_OBJECT_MAP.get(type.getCanonicalText());
    }

    private Object psiArrayTypeProcess(PsiArrayType type, Project project, Map<String, Integer> recursionMap) {
        Object defaultValue = typeDefaultValue(type.getComponentType(), project, recursionMap);
        return defaultValue == null ? null : List.of(defaultValue);
    }

    private Object psiClassReferenceTypeProcess(PsiClassReferenceType type, Project project, Map<String, Integer> recursionMap) {
        PsiClass psiClass = type.resolve();
        if (Objects.isNull(psiClass)) {
            return null;
        }

        if (psiClass.isEnum()) {
            return Arrays.stream(psiClass.getFields())
                    .filter(PsiEnumConstant.class::isInstance)
                    .map(PsiField::getName)
                    .findFirst()
                    .orElse("");
        }
        // 处理 String
        Object o1 = BASIC_DATA_TYPE_OBJECT_MAP.get(type.getCanonicalText());
        if (o1 != null) {
            return o1;
        }

        // 处理集合
        String canonicalText = type.getCanonicalText();
        if (canonicalText.startsWith("java.util.")) {
            // Map 直接返回空 Map, 反正不知道 key
            if (canonicalText.contains("Map")) {
                return Collections.emptyMap();
            }
            // 如果是 List/Set 考虑泛型
            if (canonicalText.contains("List") || canonicalText.contains("Set")) {
                String genericsType = genericsType(type);
                // 如果泛型是 java.lang.Object
                if (Object.class.getName().equals(genericsType)) {
                    return EMPTY_ARRAY;
                }
                // 基础类型的包装类
                Object o = BASIC_DATA_TYPE_OBJECT_MAP.get(genericsType);
                if (Objects.nonNull(o)) {
                    return List.of(o);
                }

                // List<? super String> / Set<? extends String> / Set<String>
                if (CharSequenceUtil.isNotEmpty(genericsType)) {
                    String actualType = getActualType(genericsType);
                    PsiClassType classType = PsiType.getTypeByName(actualType, project, GlobalSearchScope.allScope(project));
                    Object defaultValue = typeDefaultValue(classType, project, recursionMap);
                    return Objects.isNull(defaultValue) ? EMPTY_ARRAY : List.of(defaultValue);
                }
            }
        }

        // 处理日期
        String qualifiedName = psiClass.getQualifiedName();
        if (qualifiedName != null) {
            if (qualifiedName.equals(Date.class.getName()) || qualifiedName.equals(LocalDateTime.class.getName())) {
                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
            }
            if (qualifiedName.equals(LocalDate.class.getName())) {
                return new SimpleDateFormat("yyyy-MM-dd").format(Calendar.getInstance().getTime());
            }
        }

        // 类所有对象属性
        Map<String, Object> result = new LinkedHashMap<>();
        List<PsiField> objectProperties = classMemberFields(psiClass);
        Integer orDefault = recursionMap.getOrDefault(type.getCanonicalText(), 0);
        if (orDefault < 2) {
            orDefault = orDefault + 1;
            recursionMap.put(type.getCanonicalText(), orDefault);
            for (PsiField property : objectProperties) {
                String propertyName = property.getName();
                // 是否有 @JsonProperty 属性
                if (property.hasAnnotation(JSON_PROPERTY_ANNO_FQN)) {
                    propertyName = annotationValue(property.getAnnotation(JSON_PROPERTY_ANNO_FQN), new String[]{"value"});
                }

                result.put(propertyName, typeDefaultValue(property.getType(), project, recursionMap));

            }
        }
        return result;
    }

    private @NotNull String getActualType(String genericsType) {
        int subIdx = -1;
        if (genericsType.contains(" extends ")) {
            subIdx = genericsType.indexOf(" extends ") + " extends ".length();
        } else if (genericsType.contains(" super ")) {
            subIdx = genericsType.indexOf(" super ") + " super ".length();
        }
        return subIdx != -1 ? genericsType.substring(subIdx).trim() : genericsType;
    }

    private String genericsType(@NotNull PsiType psiType) {
        String canonicalText = psiType.getCanonicalText();
        Matcher matcher = GENERICS_PATTERN.matcher(canonicalText);
        return matcher.find() ? matcher.group(1) : "";
    }

    private List<PsiField> classMemberFields(@NotNull PsiClass psiClass) {
        return Arrays.stream(psiClass.getAllFields())
                .filter(field -> !hasStaticModifier(field.getModifierList()))
                .filter(field -> !hasPublicModifier(field.getModifierList()))
                .toList();
    }

    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    private boolean hasStaticModifier(@Nullable PsiModifierList target) {
        return hasModifier(target, PsiModifier.STATIC);
    }

    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
    private boolean hasPublicModifier(@Nullable PsiModifierList target) {
        return hasModifier(target, PsiModifier.PUBLIC);
    }

    private boolean hasModifier(@Nullable PsiModifierList target, @PsiModifier.ModifierConstant @NotNull String modifier) {
        return Objects.nonNull(target) && target.hasModifierProperty(modifier);
    }
    // endregion java

    // region kotlin
    @SuppressWarnings("deprecation")
    private Object ktTypeReferenceProcess(KtTypeReference typeReference, Project project, Map<String, Integer> recursionMap) {
        if (Objects.isNull(typeReference)) {
            return null;
        }
        PsiElement userType = removeNullable(typeReference.getFirstChild());
        String typeText = userType.getText();
        Object defaultValue = BASIC_DATA_TYPE_OBJECT_MAP.get(typeText);
        if (Objects.nonNull(defaultValue)) {
            return defaultValue;
        }
        if (typeText.contains("Map")) {
            return Collections.emptyMap();
        }
        if (typeText.contains("Array<") || typeText.contains("List<") || typeText.contains("Set<")) {
            // 泛型
            PsiElement element = userType.getLastChild();
            if (element instanceof KtTypeArgumentList typeArgumentList) {
                List<KtTypeProjection> arguments = typeArgumentList.getArguments();
                if (!arguments.isEmpty()) {
                    KtTypeProjection typeProjection = arguments.get(0);
                    KtTypeReference projectionTypeReference = typeProjection.getTypeReference();
                    if (projectionTypeReference != null) {
                        defaultValue = typeDefaultValue(projectionTypeReference, project, recursionMap);
                        return defaultValue == null ? Collections.emptyList() : Collections.singletonList(defaultValue);
                    } else {
                        return Collections.emptyList();
                    }
                }
            }
        }

        if ("Date".equals(typeText) || "LocalDateTime".equals(typeText)) {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().getTime());
        }
        if ("LocalDate".equals(typeText)) {
            return new SimpleDateFormat("yyyy-MM-dd").format(Calendar.getInstance().getTime());
        }

        Map<String, Object> result = new LinkedHashMap<>();
        Integer orDefault = recursionMap.getOrDefault(typeText, 0);
        if (orDefault < 2) {
            List<KtClass> ktClasses = KotlinClassShortNameIndex.getInstance().get(typeText, project, GlobalSearchScope.allScope(project)).stream()
                    .filter(KtClass.class::isInstance)
                    .map(KtClass.class::cast)
                    .filter(o -> !o.isAnnotation() && !o.isInterface())
                    .toList();
            if (!ktClasses.isEmpty()) {
                orDefault = orDefault + 1;
                recursionMap.put(typeText, orDefault);
                List<KtProperty> properties = ktClasses.get(0).getProperties();
                for (KtProperty property : properties) {
                    KtTypeReference propertyTypeReference = property.getTypeReference();
                    if (propertyTypeReference != null) {
                        result.put(property.getName(), typeDefaultValue(propertyTypeReference, project, recursionMap));
                    }
                }
                return result;
            }

            List<PsiClass> psiClasses = JavaShortClassNameIndex.getInstance().get(typeText, project, GlobalSearchScope.allScope(project))
                    .stream()
                    .filter(o -> !o.isAnnotationType() && !o.isInterface())
                    .toList();
            if (!psiClasses.isEmpty()) {
                List<PsiField> fields = classMemberFields(psiClasses.get(0));
                for (PsiField property : fields) {
                    String propertyName = property.getName();
                    // 是否有 @JsonProperty 属性
                    if (property.hasAnnotation(JSON_PROPERTY_ANNO_FQN)) {
                        propertyName = annotationValue(property.getAnnotation(JSON_PROPERTY_ANNO_FQN), new String[]{"value"});
                    }

                    result.put(propertyName, typeDefaultValue(property.getType(), project, recursionMap));
                }
                return result;
            }
        }
        return result;
    }

    private PsiElement removeNullable(@Nullable PsiElement element) {
        return element instanceof KtNullableType ? element.getFirstChild() : element;
    }


    // endregion kotlin

    @Override
    public final Map<String, ParamProperty> methodParametersParse(@NotNull PsiElement element, boolean isJsonPretty) {
        List<? extends PsiElement> parameters = methodParameters(element);
        Map<String, ParamProperty> map = TypeValueProcessor.getDefaultParameterMap();
        if (parameters.isEmpty()) {
            return map;
        }

        for (PsiElement parameter : parameters) {
            parameterParse(parameter, map, isJsonPretty);
        }
        return map;
    }


    @Override
    public final void postmanItemPropertyCompletion(PsiElement element, PostmanItem item) {
        Map<String, ParamProperty> paramPropertyMap = ProcessorContext.methodParametersParse(element, false);
        HttpEnum.ContentType type = (HttpEnum.ContentType) paramPropertyMap.get(CoreProcessor.REQUEST_TYPE_KEY).getDefaultValue();
        item.setContentType(type.getValue());

        List<String> queryParamKey = new ArrayList<>();
        Set<String> urlencodedKey = new HashSet<>();
        String httpMethod = item.getMethod();

        for (Map.Entry<String, ParamProperty> entry : paramPropertyMap.entrySet()) {
            String k = entry.getKey();
            ParamProperty v = entry.getValue();
            HttpEnum.ParamUsage usage = v.getParamUsage();
            switch (usage) {
                case URL -> {
                    if (httpMethod.equals(HttpEnum.HttpMethod.POST.name())) {
                        // 将参数格式化成 username=a&password=a
                        urlencodedKey.addAll(paramPropertyMap.keySet());
                    } else {
                        queryParamKey.add(k);
                    }
                }
                case BODY -> {
                    item.setMode("urlencoded");
                    if (type.equals(HttpEnum.ContentType.APPLICATION_JSON)) {
                        item.setMode("raw");
                        item.setRaw(v.getDefaultValue().toString().replace("\"", "\\\""));
                    }
                }
                default -> {
                    // 不处理
                }
            }
        }

        item.setQueryKey(queryParamKey);
        item.setUrlencodedKey(urlencodedKey);
    }
}
